import java.util.ArrayList;
import java.util.Random;


/**
 * This class represents Hopfiled Network
 * it can be built form example and given another example with noise
 * the network should fix the these example
 * 
 * @author Idan
 *
 */

public class HopfieldNetwork {

	private  int memoryMatrix [][];
	private ArrayList<Example> examples;
	private Random rand;
	
	/**
	 * c'tor. given a set of examples an hopfiled network will be built
	 * 
	 * @param examples
	 */
	public HopfieldNetwork(ArrayList<Example> examples) {
		this.examples=examples;
		buildMemoryMap();
		rand=new Random();

	}
	/**
	 * Builds the memort matrix
	 * 
	 */
	private void buildMemoryMap() {
		int exampleSize =examples.get(0).byteArray.length;
		memoryMatrix=  new int [exampleSize][exampleSize];
		
		for (int i = 0; i < exampleSize-1; i++) {
			for (int j = i+1; j < exampleSize; j++) {
				int sum =0;
				for (int k = 0; k < examples.size(); k++) {
					Example e = examples.get(k);
					if (e.byteArray[i]==e.byteArray[j])
						sum++;
					else
						sum--;
				}
				memoryMatrix[i][j]=sum;
				memoryMatrix[j][i]=sum;
			}
			
		}
	}
	/**
	 * This method returns the example corresponding the network memory
	 * 
	 * @param e
	 * @return
	 */
	public Example reconstructExample(Example e){
		
		byte [] fixedEx = e.byteArray.clone();

		int numberOfGoodBytes=0;
		while (numberOfGoodBytes<fixedEx.length) {
			numberOfGoodBytes=0;
			int [] per = randomPermutation();
			for (int i = 0; i < per.length; i++) {
				byte b = reconstructByte(fixedEx,per[i]);
				if (b==fixedEx[per[i]]){
					numberOfGoodBytes++;
				}else{
					numberOfGoodBytes=0;
					fixedEx[per[i]]=b;
				}
			}
		}
		Example newEx =new Example(fixedEx);
		newEx.digit=e.digit;
		return newEx;
	}

	/**
	 * returns the byte corresponding the network memory
	 * 
	 * @param e
	 * @param bytePosition
	 * @return
	 */
	private byte reconstructByte(byte[] e, int bytePosition) {
		int sum =0;
		for (int i = 0; i < e.length; i++) {
			if (i!=bytePosition){
				sum+= e[i]*memoryMatrix[bytePosition][i];
			}
		}
		if (sum>=0)
			return Constants.BIT_ON;
		else
			return Constants.BIT_OFF;
	}

	/**
	 * 
	 * 
	 * @return an array of int. each int represents a permutation of [1..n]
	 */
	private int[] randomPermutation() {
		int inputSize = Constants.EXAMPLE_MATIRX_SIZE*Constants.EXAMPLE_MATIRX_SIZE;
		int [] per= new int[inputSize];
		//making identity permutaion
		for (int i = 0; i < per.length; i++) {
			per[i]=i;
		}
		
		for (int i = 0; i < per.length; i++) {
			int switchPos =rand.nextInt(inputSize);
			int t = per[i];
			per[i]=per[switchPos];
			per[switchPos]=t;
		}
		
		return per;
	}

}
